#include "ScriptManager.h"
#include "../exceptions/ObjectNotFoundException.h"
#include "../exceptions/ScriptExecutionException.h"
#include <QDateTime>
#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QString>
#include <QTextStream>
#include <chrono>
#include <exception>
#include <memory>
#include <pybind11/embed.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>

// 定义py命名空间别名
namespace py = pybind11;

// 修复make_unique问题
template <typename T, typename... Args>
std::unique_ptr<T> make_unique(Args &&...args) {
  return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

bool ScriptManager::loadScript(const QString &objectId,
                               const QString &scriptPath) {
  if (objectId.isEmpty() || scriptPath.isEmpty()) {
    return false;
  }

  // 检查脚本文件是否存在
  QFileInfo fileInfo(scriptPath);
  if (!fileInfo.exists() || !fileInfo.isFile()) {
    qWarning() << "脚本文件不存在:" << scriptPath;
    return false;
  }

  return loadScriptModule(objectId, scriptPath);
}

bool ScriptManager::loadScriptModule(const QString &objectId,
                                     const QString &scriptPath) {
  try {
    // 读取脚本文件内容
    QFile file(scriptPath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
      qWarning() << "无法打开脚本文件:" << scriptPath;
      return false;
    }

    QTextStream in(&file);
    QString scriptContent = in.readAll();
    file.close();

    // 为每个脚本创建独立的模块命名空间
    // 使用对象ID作为模块名，确保唯一性
    QString moduleName = "script_module_" + objectId;
    PyModuleDef *moduleDef = new PyModuleDef();
    py::module_ module = py::module_::create_extension_module(
        moduleName.toStdString().c_str(),
        ("Module for object: " + objectId).toStdString().c_str(), moduleDef);

    // 执行脚本内容到模块的命名空间中
    py::exec(scriptContent.toStdString(), module.attr("__dict__"));

    // 存储模块和路径
    m_scriptModules[objectId] = module;
    m_scriptPaths[objectId] = scriptPath;
    m_scriptLoadTimes[objectId] = std::chrono::system_clock::now();

    return true;
  } catch (const std::exception &e) {
    qWarning() << "加载脚本失败:" << e.what();
    return false;
  }
}

bool ScriptManager::unloadScript(const QString &objectId) {
  if (objectId.isEmpty()) {
    return false;
  }

  auto pathIt = m_scriptPaths.find(objectId);
  if (pathIt != m_scriptPaths.end()) {
    m_scriptPaths.erase(pathIt);
  }

  auto moduleIt = m_scriptModules.find(objectId);
  if (moduleIt != m_scriptModules.end()) {
    m_scriptModules.erase(moduleIt);
  }

  auto timeIt = m_scriptLoadTimes.find(objectId);
  if (timeIt != m_scriptLoadTimes.end()) {
    m_scriptLoadTimes.erase(timeIt);
  }

  return true;
}

bool ScriptManager::reloadScript(const QString &objectId) {
  auto pathIt = m_scriptPaths.find(objectId);
  if (pathIt == m_scriptPaths.end()) {
    return false;
  }

  QString scriptPath = pathIt->second;
  return loadScript(objectId, scriptPath);
}

pybind11::object ScriptManager::executeFunction(const QString &objectId,
                                                const QString &functionName,
                                                const pybind11::args &args) {
  // 检查模块是否存在
  auto moduleIt = m_scriptModules.find(objectId);
  if (moduleIt == m_scriptModules.end()) {
    throw ObjectNotFoundException(objectId);
  }

  try {
    // 从模块中获取函数
    py::object func = moduleIt->second.attr(functionName.toStdString().c_str());

    // 调用函数
    return func(*args);
  } catch (const std::exception &e) {
    throw ScriptExecutionException(
        QString("执行函数 %1 失败: %2").arg(functionName, e.what()));
  }
}

pybind11::object ScriptManager::getModule(const QString &objectId) const {
  auto moduleIt = m_scriptModules.find(objectId);
  if (moduleIt == m_scriptModules.end()) {
    return pybind11::object();
  }

  return moduleIt->second;
}

bool ScriptManager::isScriptLoaded(const QString &objectId) const {
  return m_scriptModules.find(objectId) != m_scriptModules.end();
}